/*
 * @描述: 项目小工具集合
 * @Author: /songzi
 * @Date: 2019-07-11 12:52:36
 * @Last Modified by: songzi
 * @Last Modified time: 2020-03-18 18:13:31
 */

const to = promise => promise.then(data => [null, data]).catch(err => [err]);

/**
 * @description 批量格式化数据中的monent数据
 * @param {object} data form原始数据
 * @param {object} formateConfig from format配置项
 */
const batchDataFormat = (data, formatConfig) => {
  const dataKeys = Object.keys(data);
  const newData = {};
  dataKeys.forEach(key => {
    const hasMoment = Object.prototype.toString.call(data[key]) === '[object Array]' ? data[key][0] : data[key];
    if (!(hasMoment instanceof Object) || hasMoment.constructor.name !== 'Moment') {
      newData[key] = data[key];
      return;
    }
    const formatObj = formatConfig.filter(item => item.name === key);
    const format = formatObj.length ? formatObj[0].format : 'YYYY-MM-DD';
    if (!Array.isArray(data[key])) {
      newData[key] = data[key].format(format);
      return;
    }
    newData[key] = data[key].map(item => item.format(format));
  });
  return newData;
};
/**
 * @description 从配置项中获取单独一项配置属性
 * @param {object} config 配置项原始数据
 * @param {string} fildeName 配置项名称
 * @returns
 */
const getFormatFromConfig = (config, fildeName) => {
  const filterFormat = config.filter(item => item[fildeName]);
  const filterMap = filterFormat.map(item => {
    const { name } = item;
    const fildeVal = item[fildeName];
    return {
      name,
      [fildeName]: fildeVal,
    };
  });
  return filterMap;
};

/**
 * 空闲控制 返回函数连续调用时，空闲时间必须大于或等于 wait，func 才会执行
 *
 * @param  {function} func        传入函数，最后一个参数是额外增加的this对象，.apply(this, args) 这种方式，this无法传递进函数
 * @param  {number}   wait        表示时间窗口的间隔
 * @param  {boolean}  immediate   设置为ture时，调用触发于开始边界而不是结束边界
 * @return {function}             返回客户调用函数
 */
function debounce(func, wait, immediate) {
  let timeout;
  let args;
  let context;
  let timestamp;
  let result;
  function later() {
    // 据上一次触发时间间隔
    const last = Number(new Date()) - timestamp;

    // 上次被包装函数被调用时间间隔last小于设定时间间隔wait
    if (last < wait && last > 0) {
      timeout = setTimeout(later, wait - last);
    } else {
      timeout = null;
      // 如果设定为immediate===true，因为开始边界已经调用过了此处无需调用
      if (!immediate) {
        result = func.call(context, ...args, context);
        if (!timeout) {
          context = null;
          args = null;
        }
      }
    }
  }
  return function set(..._args) {
    // console.log(_args);
    context = this;
    args = _args;
    timestamp = Number(new Date());
    const callNow = immediate && !timeout;
    // 如果延时不存在，重新设定延时
    if (!timeout) {
      timeout = setTimeout(later, wait);
    }
    if (callNow) {
      result = func.call(context, ...args, context);
      context = null;
      args = null;
    }
    return result;
  };
}

/**
 * @description 初始化table配置中的total
 * @param {*} columns
 * @returns
 */
const initTotalList = columns => {
  const totalList = [];
  columns.forEach(column => {
    if (column.needTotal) {
      totalList.push({ ...column, total: 0 });
    }
  });
  return totalList;
};

/**
 * @description 获取obj的值
 * @param {*} obj
 */
const getValue = obj =>
  Object.keys(obj)
    .map(key => obj[key])
    .join(',');

/**
 * @description 展开树
 * @param {*} tree 树结构对象
 */
const breadthSearch = tree => {
  let stark = [];
  stark = stark.concat(tree);
  while (stark.length) {
    const temp = stark.shift();
    if (temp.child) {
      stark = stark.concat(temp.child);
    }
  }
  return stark;
};

/**
 * @description 树结构查询
 * @param {*} key
 * @param {*} value
 * @param {*} dataArry
 */
const treeQuery = (key, value, dataArry) => {
  let stark = [];
  stark = stark.concat(dataArry);
  while (stark.length) {
    const temp = stark.shift();
    if (temp.child) {
      stark = stark.concat(temp.child);
    }
    if (temp[key] === value) {
      return temp;
    }
  }
  return {};
};

/**
 * @description 解析数据中的某一项为option数据
 * @param {Array} data data数组
 * @param {String} valueType 指定作为值的字段名称
 * @param {String} nameType 指定作为lable的字段名称
 * @returns
 */
const revOption = (data = [], valueType, nameType) => {
  const optionValue = data.map(item => item[valueType]);
  const optionName = data.map(item => item[nameType]);
  return { optionValue, optionName };
};

/**
 * @description 新解析opthon方法
 * @param {*} [data=[]]
 * @param {*} [typeObj={}]
 * @returns
 */
const newRevOption = (data = [], typeObj = {}) => {
  const { value, name } = typeObj;
  const optionValue = [];
  const optionName = [];
  data.forEach(item => {
    optionValue.push(item[value]);
    optionName.push(item[name]);
  });
  return { optionValue, optionName };
};

const fraction = (num1, num2) => {
  function gcd(a, b) {
    // 欧几里德算法
    return b === 0 ? a : gcd(b, a % b);
  }
  const aNum = num1;
  const bNum = num2;
  // 约分操作
  if (aNum === 0 || bNum === 1) return 0; // 如果分子是0或分母是1就不用约分了
  const e = gcd(aNum, bNum);
  const newANum = aNum / e;
  const newBNum = bNum / e;
  return newANum / newBNum === 1 ? 1 : `${newANum}/${newBNum}`;
};

/*
 * 数字序号转字符串序号  0 => "A"
 */
const indexToString = index => {
  let charcode;
  return index
    .toString(26)
    .split('')
    .map(item => {
      charcode = item.charCodeAt(0);
      charcode += charcode >= 97 ? 10 : 49;
      return String.fromCharCode(charcode);
    })
    .join('')
    .toUpperCase();
};

/*
 * 字符串序号转数字序号  "A" => 0
 */
const stringToIndex = str => {
  let charcode;
  return parseInt(
    str
      .toLowerCase()
      .split('')
      .map(item => {
        charcode = item.charCodeAt(0);
        charcode -= charcode < 107 ? 49 : 10;
        return String.fromCharCode(charcode);
      })
      .join(''),
    26,
  );
};

export function transPaginationProperty(dataPag, map) {
  const mapKey = Object.keys(map);
  const mapObj = {};
  mapKey.forEach(item => {
    mapObj[item] = dataPag[map[item]];
  });
  const { pageSize, current, total } = { ...dataPag, ...mapObj };
  return { pageSize, current, total };
}
/**
 * @description 转换json为formdata
 * @author songs
 * @date 2019-09-03
 * @export
 * @param {*} queryObj
 * @returns
 */
export function transFormData(queryObj) {
  const mapKey = Object.keys(queryObj);
  const formData = new FormData();
  mapKey.forEach(item => {
    formData.append(item, queryObj[item]);
  });
  return formData;
}

/**
 * @description 解析path为数组
 * @author songs
 * @date 2019-09-03
 * @export
 * @path {*} str
 * @returns
 */
export function transPathToArr(path) {
  if (!path || !/(\w+\/?)+$/.test(path)) {
    throw new Error('pathErr');
  }
  const $path = path.replace(/^\/|\/+$/g, '');
  const $queryArr = $path.match(/(\?.*)/g);
  let $query = '';
  if ($queryArr && $queryArr.length) $query = $queryArr[0].replace(/^\?/, '');
  const pathArr = $path.split('/');
  return {
    pathArr,
    query: $query,
  };
}

export function revFileIcon(type, fileName) {
  if (type === 'img') return 'file-image';
  if (type === 'file') {
    const $fileaxf = /\.[^.]+$/.exec(fileName);
    const fileType = $fileaxf ? $fileaxf[0] : '';
    switch (fileType) {
      case '.zip':
        return 'file-zip';
      case '.md':
        return 'file-markdown';
      case '.apk':
        return 'android';
      case '.ipa':
        return 'apple';
      case '.docx':
      case '.doc':
        return 'file-word';
      case '.xlsx':
        return 'file-excel';
      case '.js':
      case '.css':
      case '.txt':
      case '.java':
      case '.html':
        return 'file-text';
      default:
        return 'file';
    }
  }
  return type;
}

export function formatBytes(bytes, decimals = 2) {
  if (bytes === 0) return '0 Bytes';
  const k = 1024;
  const dm = decimals < 0 ? 0 : decimals;
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
}

export function funDownload(src, name) {
  // 创建隐藏的可下载链接
  const eleLink = document.createElement('a');
  eleLink.download = name || src.substr(src.lastIndexOf('/') + 1);
  eleLink.style.display = 'none';
  // // 字符内容转变成blob地址
  eleLink.href = src;
  // // 触发点击
  document.body.appendChild(eleLink);
  eleLink.click();
  // // 然后移除
  document.body.removeChild(eleLink);
}

export function jCookie() {
  const cookies = {};
  cookies.getCookie = name => {
    const reg = new RegExp(`(^| )${name}=([^;]*)(;|$)`);
    const arr = document.cookie.match(reg);
    if (arr !== null) return decodeURIComponent(arr[2]);
    return null;
  };
  cookies.setCookie = (name, value, day) => {
    const days = day || 30;
    const exp = new Date();
    exp.setTime(exp.getTime() + days * 24 * 60 * 60 * 1000);
    document.cookie = `${name}=${encodeURIComponent(value)};expires=${exp.toGMTString()}`;
  };
  cookies.delCookie = name => {
    const exp = new Date();
    exp.setTime(exp.getTime() - 1);
    const val = cookies.getCookie(name);
    if (val !== null) {
      document.cookie = `${name}=${val};expires=${exp.toGMTString()}`;
    }
  };
  return cookies;
}

export function loacalStorageTool() {
  const storage = window.localStorage;
  const storageFun = {};
  storageFun.getStorage = name => {
    const itemVal = storage.getItem(name);
    if (!itemVal) return null;
    let $val = itemVal;
    try {
      $val = JSON.parse(itemVal);
    } catch (error) {
      return null;
    }
    return $val;
  };
  storageFun.setStorage = (name, val) => {
    let $val = val;
    $val = JSON.stringify(val);
    storage.setItem(name, $val);
  };
  storageFun.delStorage = name => {
    storage.removeItem(name);
  };
  return storageFun;
}

export function arrayGroup(arr = [], groupBy = '', comments = []) {
  const groupArr = [];
  arr.forEach(item => {
    const groupVal = item[groupBy];
    const hasGroup = groupArr.filter(items => items[groupBy] === groupVal);
    if (hasGroup.length) return hasGroup[0].child.push(item);
    const pushItem = { [groupBy]: groupVal, child: [item] };
    comments.forEach(key => {
      pushItem[key] = item[key];
    });
    groupArr.push(pushItem);
    return '';
  });
  return groupArr;
}

export function validCertNo(input) {
  let num = input.toUpperCase();
  // 身份证号码为15位或者18位，15位时全为数字，18位前17位为数字，最后一位是校验位，可能为数字或字符X。
  if (!/(^\d{15}$)|^\d{17}([0-9]|X)$/.test(num)) {
    // alert('输入的身份证号长度不对，或者号码不符合规定！\n15位号码应全为数字，18位号码末位可以为数字或X。');
    return false;
  }
  // 校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
  // 下面分别分析出生日期和校验位
  let re;
  let arrSplit;
  let dtmBirth;
  let bGoodDay;
  let arrInt;
  let arrCh;
  let nTemp;
  let i;
  const len = num.length;
  if (len === 15) {
    re = new RegExp(/^(\d{6})(\d{2})(\d{2})(\d{2})(\d{3})$/);
    arrSplit = num.match(re);
    dtmBirth = new Date(`19${arrSplit[2]}/${arrSplit[3]}/${arrSplit[4]}`); // 检查生日日期是否正确
    bGoodDay = dtmBirth.getYear() === Number(arrSplit[2]) && dtmBirth.getMonth() + 1 === Number(arrSplit[3]) && dtmBirth.getDate() === Number(arrSplit[4]);
    if (!bGoodDay) {
      // alert('输入的身份证号里出生日期不对！');
      return false;
    }
    // 将15位身份证转成18位
    // 校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
    arrInt = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
    arrCh = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
    nTemp = 0;
    num = `${num.substr(0, 6)}19${num.substr(6, num.length - 6)}`;
    for (i = 0; i < 17; i += 1) {
      nTemp += num.substr(i, 1) * arrInt[i];
    }
    num += arrCh[nTemp % 11];
    return num;
  }
  if (len === 18) {
    re = new RegExp(/^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$/);
    arrSplit = num.match(re);
    // 检查生日日期是否正确
    dtmBirth = new Date(`${arrSplit[2]}/${arrSplit[3]}/${arrSplit[4]}`);
    bGoodDay = dtmBirth.getFullYear() === Number(arrSplit[2]) && dtmBirth.getMonth() + 1 === Number(arrSplit[3]) && dtmBirth.getDate() === Number(arrSplit[4]);
    if (!bGoodDay) {
      // alert('输入的身份证号里出生日期不对！');
      return false;
    }
    // 检验18位身份证的校验码是否正确。
    // 校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
    // let valnum;
    arrInt = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
    arrCh = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
    nTemp = 0;
    for (i = 0; i < 17; i += 1) {
      nTemp += num.substr(i, 1) * arrInt[i];
    }
    const valnum = arrCh[nTemp % 11];
    if (valnum !== num.substr(17, 1)) {
      // alert('18位身份证的校验码不正确！应该为：' + valnum);
      return false;
    }
    return num;
  }
  return false;
}

export {
  batchDataFormat,
  getFormatFromConfig,
  debounce,
  getValue,
  initTotalList,
  treeQuery,
  revOption,
  fraction,
  indexToString,
  stringToIndex,
  newRevOption,
  to,
};
